#include "EngineController.h"
#include "ProgramState.h"

#include "../gfx/RenderEngine.h"
#include "../shared/Time.h"
#include "../shared/logger.h"
#include "../phy/phycccp.h"
#include "../multimedia/SoundManager/SoundManager/include/SoundManager.h"
#include "../utils/profiler.h"

namespace Putin{

std::map<const char*, float> cullMap;

EngineController::EngineController(void)
{
	mProgram = NULL;
	mNextProgram = NULL;
	mInited = false;
	mContinue = true;
	mInput = NULL;
	elapsedSinceSoundUpdate = 0.0;
}

EngineController::~EngineController(void)
{
	// Clean-up
}

bool EngineController::terminate()
{
	assert(mInited);

	delete RenderEngine::instancePtr(); // terminate Render Engine
	delete SoundManager::instancePtr();
	//TODO: delete subsystems... 
	return true;
}

bool EngineController::init()
{
	// Init all System needed..
	RenderEngine* re = new RenderEngine(); // create one global instance
	RenderEngine::instance().init(); // one-time init

	mInput = new InputManager();
	InputManager::instance().addKeyboardCallback(new InputFunctor<EngineController>(&EngineController::keyEvent,this));
	InputManager::instance().addMouseClickCallback(new InputFunctor<EngineController>(&EngineController::mouseEvent,this));

	// Start Phycccp
	//TODO: needed?

	// Init sound manager
	SoundManager::instance().init(0.1f);
	cullMap["quiet"]  = 150.0f;
	cullMap["normal"] = 250.0f;
	cullMap["loud"]   = 1000.0f;
	SoundManager::instance().enableDistanceCulling(&cullMap);
	SoundManager::instance().disableDopplerEffect();

	// TODO: other init here..

	mInited = true; // done initializing
	return true;
}

bool EngineController::preloadState(ProgramState* prog)
{
	if( !prog->loadResources())
	{
		Logger::instance().logg("Could not per-load resources..");
		return false;  	
	}
	mNextProgram = prog;
	return true;
}

bool EngineController::changeState()
{
	if( mProgram )
	{
		// there is a current program. Exit that one and go

		mProgram->terminate();
		
		// Delete the program? Prolly not?
	
	}
	mProgram = mNextProgram;

	if(!mProgram->initialize())
		return false;

	return true;
}

bool EngineController::render()
{
	if(!mProgram->render())
	{
		// maybe logg?
		Logger::instance().loggLine("Rendering failed..");
		return false;
	}

	return true;
}

bool EngineController::start()
{
	
	// not really sure if the update loop should be here, but why not?

	// One frame first, to set up everything in the renderer etc

	// Check to quit?
	while(mContinue)
	{
		startProfile("Entire Frame");
		// is renderer running
		if(RenderEngine::instance().mWindow->isClosed())
			break;

		startProfile("EngineController::update");
		if(! update() )
		{
			// Deal with it.. somehow. 
			// can things go very wrong here?
		}
		stopProfile("EngineController::update");
		startProfile("EngineController::render");
		if(! render() )
		{
			// DEAL WITH IT. 
			// Not sure how and what can fail..
		}
		stopProfile("EngineController::render");
		stopProfile("Entire Frame");
	}

	return true;
}

void EngineController::quit()
{
	mContinue = false;
}

bool EngineController::update()
{
	bool ret = true;
	if(! InputManager::instance().update())
	{
		Logger::instance().loggLine("Input manager update failed..");
		return false;
	}

//	SoundManager::instance().update();
	double elapsedTime = Time::instance().getLastFrameTime();
	elapsedSinceSoundUpdate += elapsedTime;
	if(elapsedSinceSoundUpdate > 0.000)
	{
		elapsedSinceSoundUpdate = 0.0;
		SoundManager::instance().update();
	}

	// Update the Frame Time variable
	// OBS! This should only be done here and not elsewhere
	Time::instance().updateFrametime();
		
	if( mProgram )
	{
		ret &= mProgram->update();
	}

	return ret;
}

void EngineController::keyEvent(Putin::InputArg ia)
{
	//TODO: Remove,  DEBUG feature
	if( ia.key == VK_ESCAPE )
	{
		quit();
	}

	if( mProgram )
	{
		mProgram->keyDown(ia.key);
	}
}

void EngineController::mouseEvent(Putin::InputArg ia)
{
	if( mProgram ) 
	{
		POINT p;
		GetCursorPos(&p);
		// Translate from Screenspace To Clientspace
		// Should be the same in fullscreen, but not in windowed mode
		// This code is WIN32 Specific!!!!!!!
		

		// Requires access to RenderEngines private variables.. 
		HWND hWnd = RenderEngine::instance().getWinHandle();
		assert(ScreenToClient(hWnd,&p)); // Win32 func
		mProgram->mouseDown(p.x,p.y,ia.key);
	}
}

} // end namespace Putin

